1 // This program is free software; you can redistribute it and/or
2 // modify it under the terms of the GNU General Public License
3 //
as published by the Free Software Foundation; either version 2
4 // of the License, or (at your option) any later version.
5 //
6 // This program
is distributed in the hope that it will be useful,
7 // but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // GNU General Public License
for more details.
10 //
11 // You should have received a copy of the GNU General Public License
12 // along with
this program; if not, write to the Free Software
13 // Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14
15
16 using
System;
17 using
System.Diagnostics;
18 using
Emgu.CV.Structure;
19 using
Emgu.CV;
20
21 namespace
FaceRecognition
22 {

23    ///
<summary>
24    ///
</summary>
25    
[Serializable]
26    
public class EigenObjectRecognizer
27    {
28       
private Image<Gray, Single>[] _eigenImages;
29       
private Image<Gray, Single> _avgImage;
30       
private Matrix<float>[] _eigenValues;
31       
private string[] _labels;
32       
private double _eigenDistanceThreshold;
33
34       ///
<summary>
35       ///
Get the eigen vectors that form the eigen space
36       ///
</summary>
37       ///
<remarks>The set method is primary used for deserialization, do not attemps to set it unless you know what you are doing</remarks>
38       
public Image<Gray, Single>[] EigenImages
39       {
40          
get { return _eigenImages; }
41          
set { _eigenImages = value; }
42       }

43
44       ///
<summary>
45       ///
Get or set the labels for the corresponding training image
46       ///
</summary>
47       
public String[] Labels
48       {
49          
get { return _labels; }
50          
set { _labels = value; }
51       }

52
53       ///
<summary>
54       ///
Get or set the eigen distance threshold.
55       ///
The smaller the number, the more likely an examined image will be treated as unrecognized object.
56       ///
Set it to a huge number (e.g. 5000) and the recognizer will always treated the examined image as one of the known object.
57       ///
</summary>
58       
public double EigenDistanceThreshold
59       {
60          
get { return _eigenDistanceThreshold; }
61          
set { _eigenDistanceThreshold = value; }
62       }

63
64       ///
<summary>
65       ///
Get the average Image.
66       ///
</summary>
67       ///
<remarks>The set method is primary used for deserialization, do not attemps to set it unless you know what you are doing</remarks>
68       
public Image<Gray, Single> AverageImage
69       {
70          
get { return _avgImage; }
71          
set { _avgImage = value; }
72       }

73
74       ///
<summary>
75       ///
Get the eigen values of each of the training image
76       ///
</summary>
77       ///
<remarks>The set method is primary used for deserialization, do not attemps to set it unless you know what you are doing</remarks>
78       
public Matrix<float>[] EigenValues
79       {
80          
get { return _eigenValues; }
81          
set { _eigenValues = value; }
82       }
83
84       
private EigenObjectRecognizer()
85       {
86       }

87
88
89       ///
<summary>
90       ///
Create an object recognizer using the specific tranning data and parameters, it will always return the most similar object
91       ///
</summary>
92       ///
<param name="images">The images used for training, each of them should be the same size. It's recommended the images are histogram normalized</param>
93       ///
<param name="termCrit">The criteria for recognizer training</param>
94       
public EigenObjectRecognizer(Image<Gray, Byte>[] images, ref MCvTermCriteria termCrit)
95          :
this(images, GenerateLabels(images.Length), ref termCrit)
96       {
97       }
98
99       
private static String[] GenerateLabels(int size)
100       {
101          String[] labels =
new string[size];
102          
for (int i = 0; i < size; i++)
103             labels[i] = i.ToString();
104          
return labels;
105       }

106
107       ///
<summary>
108       ///
Create an object recognizer using the specific tranning data and parameters, it will always return the most similar object
109       ///
</summary>
110       ///
<param name="images">The images used for training, each of them should be the same size. It's recommended the images are histogram normalized</param>
111       ///
<param name="labels">The labels corresponding to the images</param>
112       ///
<param name="termCrit">The criteria for recognizer training</param>
113       
public EigenObjectRecognizer(Image<Gray, Byte>[] images, String[] labels, ref MCvTermCriteria termCrit)
114          :
this(images, labels, 0, ref termCrit)
115       {
116       }

117
118       ///
<summary>
119       ///
Create an object recognizer using the specific tranning data and parameters
120       ///
</summary>
121       ///
<param name="images">The images used for training, each of them should be the same size. It's recommended the images are histogram normalized</param>
122       ///
<param name="labels">The labels corresponding to the images</param>
123       ///
<param name="eigenDistanceThreshold">
124       ///
The eigen distance threshold, (0, ~1000].
125       ///
The smaller the number, the more likely an examined image will be treated as unrecognized object.
126       ///
If the threshold is &lt; 0, the recognizer will always treated the examined image as one of the known object.
127       ///
</param>
128       ///
<param name="termCrit">The criteria for recognizer training</param>
129       
public EigenObjectRecognizer(Image<Gray, Byte>[] images, String[] labels, double eigenDistanceThreshold, ref MCvTermCriteria termCrit)
130       {
131          Debug.Assert(images.Length == labels.Length,
"The number of images should equals the number of labels");
132          Debug.Assert(eigenDistanceThreshold >=
0.0, "Eigen-distance threshold should always >= 0.0");
133
134          CalcEigenObjects(images,
ref termCrit, out _eigenImages, out _avgImage);
135
136          
/*
137          _avgImage.SerializationCompressionRatio =
9;
138
139          
foreach (Image<Gray, Single> img in _eigenImages)
140              //Set the compression ration to best compression. The serialized
object can therefore save spaces
141              img.SerializationCompressionRatio =
9;
142          */

143
144          _eigenValues = Array.ConvertAll<Image<Gray, Byte>, Matrix<
float>>(images,
145              
delegate(Image<Gray, Byte> img)
146              {
147                 
return new Matrix<float>(EigenDecomposite(img, _eigenImages, _avgImage));
148              });
149
150          _labels = labels;
151
152          _eigenDistanceThreshold = eigenDistanceThreshold;
153       }
154
155       
#region static methods
156       ///
<summary>
157       ///
Caculate the eigen images for the specific traning image
158       ///
</summary>
159       ///
<param name="trainingImages">The images used for training </param>
160       ///
<param name="termCrit">The criteria for tranning</param>
161       ///
<param name="eigenImages">The resulting eigen images</param>
162       ///
<param name="avg">The resulting average image</param>
163       
public static void CalcEigenObjects(Image<Gray, Byte>[] trainingImages, ref MCvTermCriteria termCrit, out Image<Gray, Single>[] eigenImages, out Image<Gray, Single> avg)
164       {
165          
int width = trainingImages[0].Width;
166          
int height = trainingImages[0].Height;
167
168          IntPtr[] inObjs = Array.ConvertAll<Image<Gray, Byte>, IntPtr>(trainingImages,
delegate(Image<Gray, Byte> img) { return img.Ptr; });
169
170          
if (termCrit.max_iter <= 0 || termCrit.max_iter > trainingImages.Length)
171             termCrit.max_iter = trainingImages.Length;
172          
173          
int maxEigenObjs = termCrit.max_iter;
174
175          
#region initialize eigen images
176          eigenImages =
new Image<Gray, float>[maxEigenObjs];
177          
for (int i = 0; i < eigenImages.Length; i++)
178             eigenImages[i] =
new Image<Gray, float>(width, height);
179          IntPtr[] eigObjs = Array.ConvertAll<Image<Gray, Single>, IntPtr>(eigenImages,
delegate(Image<Gray, Single> img) { return img.Ptr; });
180          
#endregion
181
182          avg =
new Image<Gray, Single>(width, height);
183
184          CvInvoke.cvCalcEigenObjects(
185              inObjs,
186              
ref termCrit,
187              eigObjs,
188              
null,
189              avg.Ptr);
190       }

191
192       ///
<summary>
193       ///
Decompose the image as eigen values, using the specific eigen vectors
194       ///
</summary>
195       ///
<param name="src">The image to be decomposed</param>
196       ///
<param name="eigenImages">The eigen images</param>
197       ///
<param name="avg">The average images</param>
198       ///
<returns>Eigen values of the decomposed image</returns>
199       
public static float[] EigenDecomposite(Image<Gray, Byte> src, Image<Gray, Single>[] eigenImages, Image<Gray, Single> avg)
200       {
201          
return CvInvoke.cvEigenDecomposite(
202              src.Ptr,
203              Array.ConvertAll<Image<Gray, Single>, IntPtr>(eigenImages,
delegate(Image<Gray, Single> img) { return img.Ptr; }),
204              avg.Ptr);
205       }
206       
#endregion
207
208       ///
<summary>
209       ///
Given the eigen value, reconstruct the projected image
210       ///
</summary>
211       ///
<param name="eigenValue">The eigen values</param>
212       ///
<returns>The projected image</returns>
213       
public Image<Gray, Byte> EigenProjection(float[] eigenValue)
214       {
215          Image<Gray, Byte> res =
new Image<Gray, byte>(_avgImage.Width, _avgImage.Height);
216          CvInvoke.cvEigenProjection(
217              Array.ConvertAll<Image<Gray, Single>, IntPtr>(_eigenImages,
delegate(Image<Gray, Single> img) { return img.Ptr; }),
218              eigenValue,
219              _avgImage.Ptr,
220              res.Ptr);
221          
return res;
222       }

223
224       ///
<summary>
225       ///
Get the Euclidean eigen-distance between <paramref name="image"/> and every other image in the database
226       ///
</summary>
227       ///
<param name="image">The image to be compared from the training images</param>
228       ///
<returns>An array of eigen distance from every image in the training images</returns>
229       
public float[] GetEigenDistances(Image<Gray, Byte> image)
230       {
231          
using (Matrix<float> eigenValue = new Matrix<float>(EigenDecomposite(image, _eigenImages, _avgImage)))
232             
return Array.ConvertAll<Matrix<float>, float>(_eigenValues,
233                 
delegate(Matrix<float> eigenValueI)
234                 {
235                    
return (float)CvInvoke.cvNorm(eigenValue.Ptr, eigenValueI.Ptr, Emgu.CV.CvEnum.NORM_TYPE.CV_L2, IntPtr.Zero);
236                 });
237       }

238
239       ///
<summary>
240       ///
Given the <paramref name="image"/> to be examined, find in the database the most similar object, return the index and the eigen distance
241       ///
</summary>
242       ///
<param name="image">The image to be searched from the database</param>
243       ///
<param name="index">The index of the most similar object</param>
244       ///
<param name="eigenDistance">The eigen distance of the most similar object</param>
245       ///
<param name="label">The label of the specific image</param>
246       
public void FindMostSimilarObject(Image<Gray, Byte> image, out int index, out float eigenDistance, out String label)
247       {
248          
float[] dist = GetEigenDistances(image);
249
250          index =
0;
251          eigenDistance = dist[
0];
252          
for (int i = 1; i < dist.Length; i++)
253          {
254             
if (dist[i] < eigenDistance)
255             {
256                index = i;
257                eigenDistance = dist[i];
258             }
259          }
260          label = Labels[index];
261       }

262
263       ///
<summary>
264       ///
Try to recognize the image and return its label
265       ///
</summary>
266       ///
<param name="image">The image to be recognized</param>
267       ///
<returns>
268       ///
String.Empty, if not recognized;
269       ///
Label of the corresponding image, otherwise
270       ///
</returns>
271       
public String Recognize(Image<Gray, Byte> image)
272       {
273          
int index;
274          
float eigenDistance;
275          String label;
276          FindMostSimilarObject(image,
out index, out eigenDistance, out label);
277
278          
return (_eigenDistanceThreshold <= 0 || eigenDistance < _eigenDistanceThreshold ) ? _labels[index] : String.Empty;
279       }
280    }
281 }


Gõ tìm kiếm nhanh...